AWS X-Rayによる分散アプリケーション分析をFargateで試してみた #Fargate
こんにちは、コカコーラ大好きカジです。
AWS X-RayをFargate上でどのように動かすか不明だったため、調べてみたところ、以下の資料が見つかったので、東京リージョンで構築して試してみました。 構築時のコマンドの実行結果も貼っています。
AWS X-Rayによる分散アプリケーション分析(分散トレーシング)とは
マイクロサービスが増え、パフォーマンス状況やデバッグが煩雑になっており、サービス間の相互作用やそれぞれの待機時間を把握するのは面倒な問題です。 そんな問題を解決するのが、AWS X-Rayです。
AWS X-Rayでできること
- パフォーマンスボトルネックとエラーの特定
- アプリケーション内の特定サービスへの問題を特定
- アプリケーションのユーザに対する問題のインパクトを特定
- アプリケーションのサービスコールグラフの可視化
X-Rayの動作詳細は AWS Black Belt Online Seminar 2017 AWS X-Ray
Fargateとは
サーバーやクラスターの管理が不要なコンテナの実行 = EC2不要でコンテナが実行出来るサービスです。
AWS Fargate事始め〜CloudWatch Logsにこんにちは | DevelopersIO
構築する環境の構成図
事前準備
以下が事前に準備できている必要があります。
- AWS CLIを実行できること
- ECS CLIを実行できること(インストールはAmazon ECS CLI のインストール - Amazon Elastic Container Service)
- Dockerコマンドが実行できること
- Fargateを載せるVPCが構築済みであること
- Fargateを載せるサブネットが、ECRと通信できるようになっていること
- envsubstコマンドが使えること(使えない場合は、MacOSXでは、brew upgrade gettextや、brew link --force gettextの実行が必要)
ECSクラスタの構築
作業用PCなどにgit cloneして作業します。
git clone https://github.com/aws-samples/aws-xray-fargate.git
ECSクラスタを作成します。 cluster_name=xray-fargate としました。
aws ecs create-cluster --cluster-name xray-fargate
実行結果
{ "cluster": { "clusterArn": "arn:aws:ecs:ap-northeast-1:000000000000:cluster/xray-fargate", "clusterName": "xray-fargate", "status": "ACTIVE", "registeredContainerInstancesCount": 0, "runningTasksCount": 0, "pendingTasksCount": 0, "activeServicesCount": 0, "statistics": [], "tags": [], "settings": [ { "name": "containerInsights", "value": "disabled" } ] } }
Task Roleを作成
以後は、git cloneしたフォルダのsrc下のREADME.md
をコピペしながら実施していきます。
<role_name>
と<policy_name>
を決めて実行します。
私は、以下にしてみました。
role_name = xray-fargate-taskrole policy_name = xray-fargate-task-policy
cd <git clone下ディレクトリ>/aws-xray-fargate/src export TASK_ROLE_NAME=$(aws iam create-role --role-name xray-fargate-taskrole --assume-role-policy-document file://ecs-trust-pol.json | jq -r '.Role.RoleName') export XRAY_POLICY_ARN=$(aws iam create-policy --policy-name xray-fargate-task-policy --policy-document file://xray-pol.json | jq -r '.Policy.Arn') aws iam attach-role-policy --role-name $TASK_ROLE_NAME --policy-arn $XRAY_POLICY_ARN
Task Execution Roleを作成
src下のREADME.mdを見ながら同じように実行していきます。
すでに1回でもFargateを起動しているとecsTaskExecutionRoleが作成済みのため別なTaskExecutionRole名にする必要があります。
私は、以下にしてみました。
<ecsTaskExecutionRole> = xray-fargate-ecsTaskExecutionRole
aws iam create-role --role-name xray-fargate-ecsTaskExecutionRole --assume-role-policy-document file://ecs-trust-pol.json export ECS_EXECUTION_POLICY_ARN=$(aws iam list-policies --scope AWS --query 'Policies[?PolicyName==`AmazonECSTaskExecutionRolePolicy`].Arn' | jq -r '.[]') aws iam attach-role-policy --role-name xray-fargate-ecsTaskExecutionRole --policy-arn $ECS_EXECUTION_POLICY_ARN
1行目だけ実行結果が出ます。
{ "Role": { "Path": "/", "RoleName": "xray-fargate-ecsTaskExecutionRole", "RoleId": "AROAUVADBEKEJOK5IU3WL", "Arn": "arn:aws:iam::000000000000:role/xray-fargate-ecsTaskExecutionRole", "CreateDate": "2019-09-04T11:08:11Z", "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "Service": "ecs-tasks.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } } }
Task RoleとTask Execution RoleのARNを環境変数に出力
export TASK_ROLE_ARN=$(aws iam get-role --role-name xray-fargate-taskrole --query "Role.Arn" --output text) export TASK_EXECUTION_ROLE_ARN=$(aws iam get-role --role-name xray-fargate-ecsTaskExecutionRole --query "Role.Arn" --output text)
Fargateを構築するVPCのサブネットを環境変数に指定
Fargate作成予定のVPC IDを確認しておきます。
vpc_id=vpc-00000000
Fargate構築するサブネットのIDを環境変数に指定します。
subnet-11111111
、subnet-22222222
とします。
export SUBNET_ID_1=subnet-11111111 export SUBNET_ID_2=subnet-22222222
セキュリティグループを作成
<group_name>
=xray-fargate-tasksg
にしました。
export SG_ID=$(aws ec2 create-security-group --group-name xray-fargate-sg --description xray-fargate-sg --vpc-id vpc-00000000 | jq -r '.GroupId')
aws ec2 authorize-security-group-ingress --group-id $SG_ID --protocol tcp --port 80 --cidr 0.0.0.0/0 aws ec2 authorize-security-group-ingress --group-id $SG_ID --protocol all --port all --source-group $SG_ID
service B用のALBを構築
私は以下のように変更して実行しました。
<load_balancer_name>
=xray-fargate-b-alb
<target_group_name>
=xray-fargate-b-tg
export LOAD_BALANCER_ARN=$(aws elbv2 create-load-balancer --name xray-fargate-b-alb --subnets $SUBNET_ID_1 $SUBNET_ID_2 --security-groups $SG_ID --scheme internet-facing --type application | jq -r '.LoadBalancers[].LoadBalancerArn') export TARGET_GROUP_ARN=$(aws elbv2 create-target-group --name xray-fargate-b-tg --protocol HTTP --port 8080 --vpc-id vpc-00000000 --target-type ip --health-check-path /health | jq -r '.TargetGroups[].TargetGroupArn') aws elbv2 create-listener --load-balancer-arn $LOAD_BALANCER_ARN --protocol HTTP --port 80 --default-actions Type=forward,TargetGroupArn=$TARGET_GROUP_ARN
実行結果
{ "Listeners": [ { "ListenerArn": "arn:aws:elasticloadbalancing:ap-northeast-1:000000000000:listener/app/xray-fargate-b-alb/cc80f26490abbeb3/bdf71758725d21a5", "LoadBalancerArn": "arn:aws:elasticloadbalancing:ap-northeast-1:000000000000:loadbalancer/app/xray-fargate-b-alb/cc80f26490abbeb3", "Port": 80, "Protocol": "HTTP", "DefaultActions": [ { "Type": "forward", "TargetGroupArn": "arn:aws:elasticloadbalancing:ap-northeast-1:000000000000:targetgroup/xray-fargate-b-tg/bf803125598e915a" } ] } ] }
Service BのDNS名(Endpoint)を取得し環境変数に指定
export SERVICE_B_ENDPOINT=$(aws elbv2 describe-load-balancers --load-balancer-arn $LOAD_BALANCER_ARN | jq -r '.LoadBalancers[].DNSName')
ECRにService A/BのコンテナをPush
ecs-cliが正常にできない場合は、「スイッチロール先のAWSアカウントでecs-cliを実行するときに便利なスクリプト」を最後に記載しているので適応をしてから試してみてください。
cd ./service-b/ docker build -t service-b . ecs-cli push service-b cd ../service-a/ docker build -t service-a . ecs-cli push service-a
実行結果
$ docker build -t service-b . Sending build context to Docker daemon 8.704kB Step 1/8 : FROM node:8-alpine ---> b9e61ad789af Step 2/8 : RUN mkdir -p /usr/src/app ---> Using cache ---> ecdc0707193d Step 3/8 : WORKDIR /usr/src/app ---> Using cache ---> 491885cffc88 Step 4/8 : COPY package.json /usr/src/app/ ---> Using cache ---> b46122624205 Step 5/8 : RUN npm install ---> Using cache ---> 091cf7c3a574 Step 6/8 : COPY . /usr/src/app ---> Using cache ---> 7bc2f297d1f5 Step 7/8 : EXPOSE 8080 ---> Using cache ---> 2a762037c8cd Step 8/8 : CMD [ "node", "server.js" ] ---> Using cache ---> 4e00fb1d0ea2 Successfully built 4e00fb1d0ea2 Successfully tagged service-b:latest $ ecs-cli push service-b INFO[0000] Getting AWS account ID... INFO[0001] Tagging image image=service-b repository=000000000000.dkr.ecr.ap-northeast-1.amazonaws.com/service-b tag= INFO[0001] Image tagged INFO[0001] Creating repository repository=service-b INFO[0001] Repository created INFO[0001] Pushing image repository=000000000000.dkr.ecr.ap-northeast-1.amazonaws.com/service-b tag= INFO[0015] Image pushed $ docker build -t service-a . Sending build context to Docker daemon 11.26kB Step 1/8 : FROM node:8-alpine ---> b9e61ad789af Step 2/8 : RUN mkdir -p /usr/src/app ---> Using cache ---> ecdc0707193d Step 3/8 : WORKDIR /usr/src/app ---> Using cache ---> 491885cffc88 Step 4/8 : COPY package.json /usr/src/app/ ---> a95eab0a3236 Step 5/8 : RUN npm install ---> Running in a41cd518380f npm notice created a lockfile as package-lock.json. You should commit this file. npm WARN [email protected] No repository field. npm WARN [email protected] No license field. added 85 packages from 115 contributors and audited 217 packages in 6.571s found 0 vulnerabilities Removing intermediate container a41cd518380f ---> a7f5e3006000 Step 6/8 : COPY . /usr/src/app ---> fdda478a4955 Step 7/8 : EXPOSE 8080 ---> Running in 5569a53ae866 Removing intermediate container 5569a53ae866 ---> 9fc0907cc380 Step 8/8 : CMD [ "node", "server.js" ] ---> Running in c3b88004ec36 Removing intermediate container c3b88004ec36 ---> 57513ee25141 Successfully built 57513ee25141 Successfully tagged service-a:latest HL00294:service-a kajihiroyuki$ ecs-cli push service-a INFO[0000] Getting AWS account ID... INFO[0000] Tagging image image=service-a repository=000000000000.dkr.ecr.ap-northeast-1.amazonaws.com/service-a tag= INFO[0000] Image tagged INFO[0001] Creating repository repository=service-a INFO[0001] Repository created INFO[0001] Pushing image repository=000000000000.dkr.ecr.ap-northeast-1.amazonaws.com/service-a tag= INFO[0013] Image pushed
リポジトリのURLを取得
export REGISTRY_URL_SERVICE_B=$(aws ecr describe-repositories --repository-name service-b | jq -r '.repositories[].repositoryUri') export REGISTRY_URL_SERVICE_A=$(aws ecr describe-repositories --repository-name service-a | jq -r '.repositories[].repositoryUri')
各サービス用のCloudwatch Logsのロググループを作成
aws logs create-log-group --log-group-name /ecs/service-b aws logs create-log-group --log-group-name /ecs/service-a
Service Bを構築
東京リージョンで実行する場合は、docker-compose.yml内のリージョンをus-east-1から、ap-northeast-1へ修正が必要です。 東京リージョンで構築していると、us-east-1のCloudwatch Logsに投げられないので、タスクの起動停止を繰り返します。
cd ../service-b/ envsubst < docker-compose.yml-template > docker-compose.yml # docker-compose.yml内のリージョンをus-east-1から、ap-northeast-1へ修正が必要です。 envsubst < ecs-params.yml-template > ecs-params.yml ecs-cli compose service up --deployment-max-percent 100 --deployment-min-healthy-percent 0 --target-group-arn $TARGET_GROUP_ARN --launch-type FARGATE --container-name service-b --container-port 8080 --cluster xray-fargate
実行結果 docker-compose.yml内の修正が漏れており、2回失敗しているので、service-b:3になっています。1回でできていれば、1になります。
$ ecs-cli compose service up --deployment-max-percent 100 --deployment-min-healthy-percent 0 --target-group-arn $TARGET_GROUP_ARN --launch-type FARGATE --container-name service-b --container-port 8080 --cluster xray-fargate WARN[0000] Environment variable is unresolved. Setting it to a blank value... key name=AWS_XRAY_DAEMON_ADDRESS WARN[0000] Environment variable is unresolved. Setting it to a blank value... key name=AWS_XRAY_DAEMON_ADDRESS INFO[0011] Using ECS task definition TaskDefinition="service-b:3" INFO[0011] Created an ECS service deployment-max-percent=100 deployment-min-healthy-percent=0 service=service-b taskDefinition="service-b:3" INFO[0012] Updated ECS service successfully deployment-max-percent=100 deployment-min-healthy-percent=0 desiredCount=1 force-deployment=false service=service-b INFO[0027] (service service-b) has started 1 tasks: (task 5e5cdf10-81c5-47f3-bb0a-d3d952bcdfe6). timestamp="2019-09-05 10:07:19 +0000 UTC" INFO[0072] Service status desiredCount=1 runningCount=1 serviceName=service-b INFO[0072] ECS Service has reached a stable state desiredCount=1 runningCount=1 serviceName=service-b
Service A用のALBを構築
私は以下のように変更して実行しました。
<load_balancer_name>
=xray-fargate-a-alb
<target_group_name>
=xray-fargate-a-tg
export LOAD_BALANCER_ARN=$(aws elbv2 create-load-balancer --name xray-fargate-a-alb --subnets $SUBNET_ID_1 $SUBNET_ID_2 --security-groups $SG_ID --scheme internet-facing --type application | jq -r '.LoadBalancers[].LoadBalancerArn') export TARGET_GROUP_ARN=$(aws elbv2 create-target-group --name xray-fargate-a-tg --protocol HTTP --port 8080 --vpc-id vpc-00000000 --target-type ip --health-check-path /health | jq -r '.TargetGroups[].TargetGroupArn') aws elbv2 create-listener --load-balancer-arn $LOAD_BALANCER_ARN --protocol HTTP --port 80 --default-actions Type=forward,TargetGroupArn=$TARGET_GROUP_ARN
Service AのFargateを構築
Service Bと同様に、東京リージョンで実行する場合は、docker-compose.yml内のリージョンをus-east-1から、ap-northeast-1へ修正が必要です。
cd ./service-a/ envsubst < docker-compose.yml-template > docker-compose.yml ## Service Bと同様に、東京リージョンで実行する場合は、docker-compose.yml内のリージョンをus-east-1から、ap-northeast-1へ修正が必要です。 envsubst < ecs-params.yml-template > ecs-params.yml ecs-cli compose service up --deployment-max-percent 100 --deployment-min-healthy-percent 0 --target-group-arn $TARGET_GROUP_ARN --launch-type FARGATE --container-name service-a --container-port 8080 --cluster xray-fargate
実行結果
$ ecs-cli compose service up --deployment-max-percent 100 --deployment-min-healthy-percent 0 --target-group-arn $TARGET_GROUP_ARN --launch-type FARGATE --container-name service-a --container-port 8080 --cluster xray-fargate WARN[0000] Environment variable is unresolved. Setting it to a blank value... key name=AWS_XRAY_DAEMON_ADDRESS WARN[0000] Environment variable is unresolved. Setting it to a blank value... key name=AWS_XRAY_DAEMON_ADDRESS INFO[0000] Using ECS task definition TaskDefinition="service-a:3" INFO[0001] Created an ECS service deployment-max-percent=100 deployment-min-healthy-percent=0 service=service-a taskDefinition="service-a:3" INFO[0002] Updated ECS service successfully deployment-max-percent=100 deployment-min-healthy-percent=0 desiredCount=1 force-deployment=false service=service-a INFO[0017] (service service-a) has reached a steady state. timestamp="2019-09-05 10:11:41 +0000 UTC" INFO[0047] (service service-a) has started 1 tasks: (task 9e827be2-47ad-4014-b5c6-411e7687b2ca). timestamp="2019-09-05 10:12:14 +0000 UTC" INFO[0078] Service status desiredCount=1 runningCount=1 serviceName=service-a INFO[0078] ECS Service has reached a stable state desiredCount=1 runningCount=1 serviceName=service-a
これで構築が完了します。
X-Rayのコンソールで確認
AWS X-Rayのコンソールを確認しますが、タスク分しかサービスマップに表示されません。
ApacheBenchでService AのALBへリクエスト送付
以下のコマンド
ab -n 100 -c 10 http://xray-fargate-a-alb-xxxxxxxxx.ap-northeast-1.elb.amazonaws.com/
少し待つと、意図したサービス間の遅延が確認できるX-Rayのサービスマップが表示されました。
外部からのリクエストのレスポンスをTraceの詳細で確認できます。
まとめ
X-RayをFargateで構築する場合にどのようにすると良いのか学ぶことができました。 また、ecs-cliを初めて使い、ちょっとハマりましたが、試すことができました。 どなたかのお役に立てれば光栄です。
(補足)スイッチロール先のAWSアカウントでecs-cliを実行するときに便利なスクリプト
ecs-cliでスイッチロール先のAWSアカウントで実行するときに私の環境下ではうまくできませんでした。 そのため、Security Tokenを発行してecs-cliのプロファイルに登録してくれる以下のスクリプトを使って、ecs-cliを実行しました。
https://github.com/dimisjim/bash-scripts/blob/master/AWS/ecs-cli-auth.sh